package org.apache.http.client.fluent;

import java.util.concurrent.Future;
import org.apache.http.client.ResponseHandler;
import org.apache.http.concurrent.BasicFuture;
import org.apache.http.concurrent.FutureCallback;

public class Async
{
  private Executor executor;
  private java.util.concurrent.Executor concurrentExec;

  public static Async newInstance()
  {
    return new Async();
  }

  public Async use(Executor executor)
  {
    this.executor = executor;
    return this;
  }

  public Async use(java.util.concurrent.Executor concurrentExec) {
    this.concurrentExec = concurrentExec;
    return this;
  }

  public <T> Future<T> execute(Request request, ResponseHandler<T> handler, FutureCallback<T> callback)
  {
    BasicFuture future = new BasicFuture(callback);
    ExecRunnable runnable = new ExecRunnable(future, request, this.executor != null ? this.executor : Executor.newInstance(), handler);

    if (this.concurrentExec != null) {
      this.concurrentExec.execute(runnable);
    } else {
      Thread t = new Thread(runnable);
      t.setDaemon(true);
      t.start();
    }
    return future;
  }

  public <T> Future<T> execute(Request request, ResponseHandler<T> handler) {
    return execute(request, handler, null);
  }

  public Future<Content> execute(Request request, FutureCallback<Content> callback) {
    return execute(request, new ContentResponseHandler(), callback);
  }

  public Future<Content> execute(Request request) {
    return execute(request, new ContentResponseHandler(), null);
  }

  static class ExecRunnable<T>
    implements Runnable
  {
    private final BasicFuture<T> future;
    private final Request request;
    private final Executor executor;
    private final ResponseHandler<T> handler;

    ExecRunnable(BasicFuture<T> future, Request request, Executor executor, ResponseHandler<T> handler)
    {
      this.future = future;
      this.request = request;
      this.executor = executor;
      this.handler = handler;
    }

    public void run() {
      try {
        Response response = this.executor.execute(this.request);
        Object result = response.handleResponse(this.handler);
        this.future.completed((T) result);
      } catch (Exception ex) {
        this.future.failed(ex);
      }
    }
  }
}